package com.tj.template.application.oss;

import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import com.fqgj.exception.common.ApplicationException;
import com.tj.bo.common.conf.config.FileConfigUtil;
import com.tj.bo.common.conf.enums.BasicErrorCodeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * @program: binary-option
 * @description
 * @author: binary
 * @create: 2020-06-23 15:58
 **/
@Service
public class OSSTokenService {

    private static final Logger LOGGER = LoggerFactory.getLogger(OSSTokenService.class);
    /**
     * 目前只有"cn-hangzhou"这个region可用, 不要使用填写其他region的值
     */
    public static final String STS_API_VERSION = "2015-04-01";

    @Autowired
    private FileConfigUtil fileConfigUtil;

    @Autowired
    private OSSProperties ossProperties;


    public OssAppTokenInfo assumeRole(String userCode) {
        try {
            OssInfo ossInfo = new OssInfo();
            // 创建一个 Aliyun Acs Client, 用于发起 OpenAPI 请求
            // 添加endpoint（直接使用STS endpoint，前两个参数留空，无需添加region ID）
            DefaultProfile.addEndpoint("", "", "Sts", ossProperties.getStsDomain());
            // 构造default profile（参数留空，无需添加region ID）
            IClientProfile profile = DefaultProfile.getProfile("", ossProperties.getPrivateAccessKeyId(), ossProperties.getPrivateAccessKeySecret());

            DefaultAcsClient client = new DefaultAcsClient(profile);

            String policy = ossInfo.getPolicy();
            String roleSessionName = userCode + "";
            String objectName = fileConfigUtil.isServerTest() ? "binaryClientSpace/test" + userCode : "binaryClientSpace/" + userCode;

            policy = String.format(policy, ossProperties.getBucketName(), objectName, ossProperties.getBucketName());
            ossInfo.setPolicy(policy);
            ossInfo.setRoleSessionName(roleSessionName);
            ossInfo.setBucketName(ossProperties.getBucketName());
            ossInfo.setObjectName(objectName);
            ossInfo.setBaseUrl(ossProperties.getPublicEndpoint());

            // 创建一个 AssumeRoleRequest 并设置请求参数
            final AssumeRoleRequest request = new AssumeRoleRequest();
            request.setVersion(STS_API_VERSION);
            request.setMethod(MethodType.POST);
            request.setProtocol(ossInfo.getProtocolType());
            request.setRoleArn(ossProperties.getRoleArn());
            request.setRoleSessionName(ossInfo.getRoleSessionName());
            request.setPolicy(ossInfo.getPolicy());
            request.setDurationSeconds(ossInfo.getDurationSeconds());

            // 发起请求，并得到response
            LOGGER.info("AssumeRoleRequest is {}", JSONObject.toJSON(request));
            AssumeRoleResponse response = client.getAcsResponse(request);
            OssAppTokenInfo ossTokenInfo = OssAppTokenInfo.translate(response, ossInfo);
            return ossTokenInfo;
        } catch (Exception e) {
            LOGGER.error("OSSTokenService assumeRole error.", e);
            throw new ApplicationException(BasicErrorCodeEnum.INTERNAL_SERVER_ERROR, "get assume role error");
        }
    }


    public static class OssAppTokenInfo {

        private String accessKeyId;
        private String accessKeySecret;
        private String securityToken;
        private String expiration;
        private String objectName;
        private String bucketName;
        private String baseUrl;

        public static OssAppTokenInfo translate(AssumeRoleResponse stsResponse, OssInfo ossInfo) {

            OssAppTokenInfo ossAppTokenInfo = new OssAppTokenInfo();
            ossAppTokenInfo.setAccessKeyId(stsResponse.getCredentials().getAccessKeyId());
            ossAppTokenInfo.setAccessKeySecret(stsResponse.getCredentials().getAccessKeySecret());
            ossAppTokenInfo.setSecurityToken(stsResponse.getCredentials().getSecurityToken());
            ossAppTokenInfo.setExpiration(stsResponse.getCredentials().getExpiration());
            ossAppTokenInfo.setBaseUrl(ossInfo.getBaseUrl());
            ossAppTokenInfo.setBucketName(ossInfo.getBucketName());
            ossAppTokenInfo.setObjectName(ossInfo.getObjectName());

            return ossAppTokenInfo;
        }

        public String getObjectName() {
            return objectName;
        }

        public void setObjectName(String objectName) {
            this.objectName = objectName;
        }

        public String getBaseUrl() {
            return baseUrl;
        }

        public void setBaseUrl(String baseUrl) {
            this.baseUrl = baseUrl;
        }

        public String getBucketName() {
            return bucketName;
        }

        public void setBucketName(String bucketName) {
            this.bucketName = bucketName;
        }

        public String getAccessKeyId() {
            return accessKeyId;
        }

        public void setAccessKeyId(String accessKeyId) {
            this.accessKeyId = accessKeyId;
        }

        public String getAccessKeySecret() {
            return accessKeySecret;
        }

        public void setAccessKeySecret(String accessKeySecret) {
            this.accessKeySecret = accessKeySecret;
        }

        public String getSecurityToken() {
            return securityToken;
        }

        public void setSecurityToken(String securityToken) {
            this.securityToken = securityToken;
        }

        public String getExpiration() {
            return expiration;
        }

        public void setExpiration(String expiration) {
            this.expiration = expiration;
        }
    }

    public static class OssInfo {

        /**
         * RoleSessionName 是临时Token的会话名称，自己指定用于标识你的用户，主要用于审计，或者用于区分Token颁发给谁
         * 但是注意RoleSessionName的长度和规则，不要有空格，只能有'-' '_' 字母和数字等字符
         * 具体规则请参考API文档中的格式要求
         */
        private String roleSessionName;
        private String policy = "{\n" +
                "  \"Statement\": [\n" +
                "    {\n" +
                "      \"Action\": [\n" +
                "        \"oss:PutObject\"\n" +
                "      ],\n" +
                "      \"Effect\": \"Allow\",\n" +
                "      \"Resource\": [\"acs:oss:*:*:%s/%s/*\", \"acs:oss:*:*:%s\"]\n" +
                "    }\n" +
                "  ],\n" +
                "  \"Version\": \"1\"\n" +
                "}";
        private ProtocolType protocolType = ProtocolType.HTTPS;
        private Long durationSeconds = 30 * 60L;
        private String objectName;
        private String bucketName;
        private String baseUrl;

        public String getBaseUrl() {
            return baseUrl;
        }

        public void setBaseUrl(String baseUrl) {
            this.baseUrl = baseUrl;
        }

        public String getBucketName() {
            return bucketName;
        }

        public void setBucketName(String bucketName) {
            this.bucketName = bucketName;
        }

        public String getObjectName() {
            return objectName;
        }

        public void setObjectName(String objectName) {
            this.objectName = objectName;
        }

        public void setRoleSessionName(String roleSessionName) {
            this.roleSessionName = roleSessionName;
        }

        public void setPolicy(String policy) {
            this.policy = policy;
        }

        public String getRoleSessionName() {
            return roleSessionName;
        }

        public String getPolicy() {
            return policy;
        }

        public ProtocolType getProtocolType() {
            return protocolType;
        }

        public Long getDurationSeconds() {
            return durationSeconds;
        }
    }
}
